package com.tuoyu.peopletest.view.ac;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.stresscube.usbdevice.USBDevicePDHIDWithAddress;
import com.stresscube.usbdevice.data.ParaScan;
import com.tuoyu.peopletest.R;
import com.tuoyu.peopletest.utils.Utils;

import java.util.ArrayList;

public class CalibrationFluorescenceActivity extends NomalBaseActivity implements View.OnClickListener {
    int nCalData2 = 8;
    private boolean[] mbCalibrated = new boolean[nCalData2];
    TextView mtvMessage;
    ArrayList<Integer> mListResult = new ArrayList<Integer>(); //保存读出来的数据
    int[] miResult = new int[nCalData2];
    int miCalibrate; //1-8
    int iBufferHead = 0, iBufferData1 = 8, iBufferData2 = 20;
    int nBytesHead = 8, nBytesData1 = 12, nBytesData2 = 32;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public void initContentView() {
        setContentView(R.layout.activity_calibration_fluorescence);

        mtvMessage = findViewById(R.id.tvMessage);
        InitData();
    }

    @Override
    public void initChildView() {

    }

    private void InitData() {
        /*findViewById(R.id.v_btn_calibration_1).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_2).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_3).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_4).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_5).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_6).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_7).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_8).setOnClickListener(this);*/
        findViewById(R.id.v_btn_calibration_read).setOnClickListener(this);
        findViewById(R.id.v_btn_calibration_write).setOnClickListener(this);
    }
    @Override
    protected void onStart() {
        super.onStart();
        mDeviceHID.init(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mDeviceHID.release();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            /*case R.id.v_btn_calibration_1:
                Calibration(1);
                break;
            case R.id.v_btn_calibration_2:
                Calibration(2);
                break;
            case R.id.v_btn_calibration_3:
                Calibration(3);
                break;
            case R.id.v_btn_calibration_4:
                Calibration(4);
                break;
            case R.id.v_btn_calibration_5:
                Calibration(5);
                break;
            case R.id.v_btn_calibration_6:
                Calibration(6);
                break;
            case R.id.v_btn_calibration_7:
                Calibration(7);
                break;
            case R.id.v_btn_calibration_8:
                Calibration(8);
                break;*/
            case R.id.v_btn_calibration_read:
                ReadCalibrateDataFromDevice12_02();
                break;
            case R.id.v_btn_calibration_write:
                SaveCalibrateData2Device12_02();
                break;
        }
    }

    private boolean bTestRunning = false;

    private boolean Calibration(int color) {
        miCalibrate = color;
        if (bTestRunning) {
            Toast.makeText(this, "正在校准中，请稍后", Toast.LENGTH_SHORT).show();
            return false;
        }
        new Test().start();
        return false;
    }

    void SendDispMessage(String str) {
        Message message = new Message();
        message.what = 1;
        message.obj = str;
        mHandler.sendMessage(message);
    }

    public void DisplayMessage(String msg) {
        mtvMessage.setText(msg + "\r\n" + mtvMessage.getText());
    }

    Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 1:
                    DisplayMessage((String) msg.obj);
                    break;
                case 2:
                    switch (msg.arg1) {
                       /* case 1:
                            ((TextView) findViewById(R.id.tvCalibrated1)).setText("OK");
                            break;
                        case 2:
                            ((TextView) findViewById(R.id.tvCalibrated2)).setText("OK");
                            break;
                        case 3:
                            ((TextView) findViewById(R.id.tvCalibrated3)).setText("OK");
                            break;
                        case 4:
                            ((TextView) findViewById(R.id.tvCalibrated4)).setText("OK");
                            break;
                        case 5:
                            ((TextView) findViewById(R.id.tvCalibrated5)).setText("OK");
                            break;
                        case 6:
                            ((TextView) findViewById(R.id.tvCalibrated6)).setText("OK");
                            break;
                        case 7:
                            ((TextView) findViewById(R.id.tvCalibrated7)).setText("OK");
                            break;
                        case 8:
                            ((TextView) findViewById(R.id.tvCalibrated8)).setText("OK");
                            break;*/
                    }
                    boolean bCalibrateCompleted = true;
                    for (int i = 0; i < nCalData2; i++) {
                        if (!mbCalibrated[i])
                            bCalibrateCompleted = false;
                    }
                    if (bCalibrateCompleted)
                        SaveCalibrateData2Device();
                    break;
            }
            super.handleMessage(msg);
        }
    };

    private void SaveCalibrateData2Device() {
        byte[] bytes = new byte[nBytesData2];
        int iBuffer = 0;
        for (int i = 0; i < nCalData2; i++) {
            ConvertInt2Bytes(miResult[i], bytes, iBuffer);
            iBuffer += 4;
        }
        if (mDeviceHID.WriteMemoBlock(iBufferData2, bytes))
            DisplayMessage("保存数据至设备成功");
        else
            DisplayMessage("保存数据至设备失败");
    }
    private void SaveCalibrateData2Device12_02() {
        ParaScan para=new ParaScan();

        byte start = (byte) Integer.parseInt(((EditText) findViewById(R.id.edtStart)).getText().toString());
        byte stop = (byte) Integer.parseInt(((EditText) findViewById(R.id.edtStop)).getText().toString());
        int brightness = Integer.parseInt(((EditText) findViewById(R.id.edtBrightness)).getText().toString());
        String strColor = ((EditText) findViewById(R.id.edtColor)).getText().toString();
        byte color = Utils.hexString2byte(strColor);
        para.paraScanFluorescence.start=start;
        para.paraScanFluorescence.stop=stop;
        para.paraScanFluorescence.setBrightness(brightness);
        para.paraScanFluorescence.color=color;
        para.bFluorescence=true;
        para.bColloidalGold=false;

        if (mDeviceHID.saveParaScan2Device(para))
            DisplayMessage("写数据到设备成功");
        else
            DisplayMessage("写数据到设备失败！！");
    }

    private void ReadCalibrateDataFromDevice12_02() {
        ParaScan para=mDeviceHID.readParaScanFromDevice();
        if (para==null||!para.bFluorescence) {
            DisplayMessage("从设备读校准数据失败");
            return;
        }

        ((EditText) findViewById(R.id.edtStart)).setText(String.valueOf(para.paraScanFluorescence.start));
        ((EditText) findViewById(R.id.edtStop)).setText(String.valueOf(para.paraScanFluorescence.stop));
        ((EditText) findViewById(R.id.edtBrightness)).setText(String.valueOf(para.paraScanFluorescence.getBrightness()));
        ((EditText) findViewById(R.id.edtColor)).setText(Utils.byte2hexString(para.paraScanFluorescence.color));
        DisplayMessage("从设备读校准数据成功");
    }

    private void ConvertInt2Bytes(int data, byte[] bytes, int iBuffer) {
        bytes[iBuffer] = (byte) ((data & 0xFF000000) >> 24);
        bytes[iBuffer + 1] = (byte) ((data & 0xFF0000) >> 16);
        bytes[iBuffer + 2] = (byte) ((data & 0xFF00) >> 8);
        bytes[iBuffer + 3] = (byte) (data & 0xFF);
    }

    private int GetIntFromBytes(byte[] bytes, int iBuffer) {
        if (bytes == null || bytes.length <= iBuffer + 4)
            return -1;
        return (((bytes[iBuffer] & 0xFF) * 256 + (bytes[iBuffer + 1] & 0xff)) * 256 + (bytes[iBuffer + 2] & 0xff)) * 256 + (bytes[iBuffer + 3] & 0xff);
    }

    USBDevicePDHIDWithAddress mDeviceHID = new USBDevicePDHIDWithAddress();
    byte ledPosition = (byte) 0x03; //孔位为3
    byte mADPosition = (byte) 0x01; //AD孔位
    int mBrightness = 600;
    int mStart = 25;
    int mStop = 125;
    int mWaveLength = 1;

    private boolean LEDOn() {
        switch (mWaveLength) {
            case 0:
                return OpenLED365();
            case 1:
                return OpenLED470();
            case 2:
                return OpenLED525_1();
            case 3:
                return OpenLED525_2();
            default:
                return OpenLED470();
        }
    }

    private boolean LEDOff() {
        if (!mDeviceHID.LEDOn(ledPosition, (byte) 0x00, 0)) {
            SendDispMessage("关灯失败");
            return true;
        }
        SendDispMessage("关灯成功");
        return true;
    }

    private boolean OpenLED(byte color) {
        if (!mDeviceHID.LEDOn(ledPosition, color, mBrightness)) {
            SendDispMessage("开灯失败");
            return false;
        }
        SendDispMessage("开灯成功");
        return true;
    }

    private boolean OpenLED365() {
        return OpenLED((byte) 0x15);
    }

    private boolean OpenLED470() {
        return OpenLED((byte) 0x2a);
    }

    private boolean OpenLED525_1() {
        return OpenLED((byte) 0x40);
    }

    private boolean OpenLED525_2() {
        return OpenLED((byte) 0x80);
    }

    class Test extends Thread {
        @Override
        public void run() { //改成发移动命令、连续获取AD、检测停止命令
            do {
                mDeviceHID.Skip();
                OpenLED470();
                SendDispMessage("开始测试");
                if (!mDeviceHID.MoveTray(0)) {
                    SendDispMessage("失败，测试退出");
                    break;
                }
                SendDispMessage("开灯");
                if (!LEDOn()) break;
                mListResult.clear();
                SendDispMessage("移动到起点");
                if (!mDeviceHID.MoveTray(mStart)) {
                    SendDispMessage("失败，测试退出");
                    break;
                }
                if (!mDeviceHID.StartADReceive(mADPosition)) break;
                if (mDeviceHID.GetADAfterStart() < 0) break;
                if (mDeviceHID.GetADAfterStart() < 0) break;
                SendDispMessage("开始扫描");
                if (!mDeviceHID.StartMoveTray(mStop)) {
                    SendDispMessage("失败，测试退出");
                    break;
                }
                while (true) { //连续读
                    int data = mDeviceHID.GetADAfterStart();
                    if (data <= 0) {
                        if (!mDeviceHID.StopADReceive()) break;
                        SendDispMessage("结束扫描");
                        break;
                    }
                    mListResult.add(data);
                }
                if (!LEDOff()) break;
                mDeviceHID.MoveTray(0);
                SendDispMessage("结束测试");
                super.run();

                if (mListResult.size() > 10) {
                    int[] datas = new int[mListResult.size()];
                    for (int i = 0; i < mListResult.size(); i++)
                        datas[i] = mListResult.get(i);
                    int[] areas = new int[2];
                    if (!GetArea(areas, datas, 1))
                        break;
                    miResult[miCalibrate] = areas[0];
                    if (miResult[miCalibrate] <= 0)
                        break;
                    SendDispMessage("测试结果：" + miResult[miCalibrate]);
                    SendCalibrateOK();
                }
            } while (false);
            LEDOff();
            mDeviceHID.MoveTray(0);
            bTestRunning = false;
        }
    }

    void SendCalibrateOK() {
        Message msg = new Message();
        msg.what = 2;
        msg.arg1 = miCalibrate;
        mHandler.sendMessage(msg);
    }

    //给定数组，排除掉最大值和最小值后，取平均值，如果最大值和最小值在平均值上下20%以内，返回，否则继续
    private int GetResult(int[] r) {
        double upper = 1.2, lower = 0.8;
        while (true) {
            int iMax = -1, iMin = -1, max = -1, min = -1;
            double mean = 0;
            int nData = 0;
            for (int i = 0; i < r.length; i++) {
                if (r[i] == -1)
                    continue;
                mean += r[i];
                nData++;
                if (max == -1 || max < r[i]) {
                    max = r[i];
                    iMax = i;
                }
                if (min == -1 || min > r[i]) {
                    min = r[i];
                    iMin = i;
                }
            }
            mean = mean / nData;
            if (max <= mean * upper && min >= mean * lower)
                return (int) (mean + 0.5);
            if (nData == 0)
                return -1;
            r[iMax] = -1;
            r[iMin] = -1;
        }
    }

    //计算峰值下的面积，一共2个峰，峰值左右各1mm，确定是向上的峰或向下的谷
    //左向外10个点及右向外10个点，取平均值，确定为基线
    int left = 10, right = 10, minGap = 50;//面积计算时，极值点左右的距离, minGap为两个峰值间的距离， 单位为0.1mm

    private boolean GetArea(int[] area, int[] data, int step) {
        if (area == null || area.length != 2)
            return false;
        int max1 = 0, max2 = 0, max2Left = 0, max2Right = 0;
        int iMax1 = 0, iMax2 = 0;
//        if (mbValley) {
//            for (int iData = 0; iData < data.length; iData++) {
//                data[iData] *= -1;
//            }
//            max2Left = max2Right = max1 = -256 * 256 * 256 * 127;
//        }
        //找第1个峰
        for (int iData = 0; iData < data.length; iData++)
            if (data[iData] > max1) {
                max1 = data[iData];
                iMax1 = iData;
            }
        int area1 = CalcArea(data, iMax1, step);
        //找第2个峰
        int iMax2Left = 0, iMax2Right = 0;
        if (iMax1 * step > (left + right) + left) {
            for (int iData = left / step; iData < iMax1 - (left + right) / step; iData++)
                if (data[iData] > max2Left) {
                    max2Left = data[iData];
                    iMax2Left = iData;
                }
        }
        if ((data.length - iMax1) * step > (left + right) + right) {
            for (int iData = iMax1 + (left + right) / step; iData < data.length - right / step; iData++)
                if (data[iData] > max2Right) {
                    max2Right = data[iData];
                    iMax2Right = iData;
                }
        }
        if (max2Right > max2Left) {
            max2 = max2Right;
            iMax2 = iMax2Right;
        } else {
            max2 = max2Left;
            iMax2 = iMax2Left;
        }
        int area2 = CalcArea(data, iMax2, step);
        if (max2Right < max2Left) { //如果第2个峰在左边，则将area1和area2换位置
            int areax = area1;
            area1 = area2;
            area2 = areax;
        }
        area[0] = area1;
        area[1] = area2;
        return true;
    }

    //给定点，算左右1mm的面积
    //首先计算左点外10个点和右点外10个点的平均值
    //再计算左右1mm内值减去平均值后的面积
    private int CalcArea(int[] data, int iMax, int step) {
        int average = 0;
        int nData = 0, sum = 0;
        for (int iData = iMax - left / step - 10; iData < iMax - left / step; iData++) {
            if (iData <= 0)
                continue;
            sum += data[iData];
            nData++;
        }
        for (int iData = iMax + right / step; iData < iMax + right / step + 10; iData++) {
            if (iData >= data.length)
                break;
            sum += data[iData];
            nData++;
        }
        if (nData == 0)
            return -1;
        average = sum / nData;
        int area = 0;
        for (int iData = iMax - left / step; iData < iMax + right / step; iData++) {
            if (iData < 0 || iData >= data.length) {
                Log.d("area", "Data index error:" + iData);
                continue;
            }
            area += (data[iData] - average) * step;
        }
        return area;
    }

}